/*匹配管理模块*/

#ifndef __M_MATCHER_H__
#define __M_MATCHER_H__
#include <mutex>
#include <list>
#include <condition_variable>
#include <thread>
#include "util.hpp"
#include "room.hpp"
#include "online.hpp"

/*匹配队列---玩家匹配*/
template <class T>
class match_queue 
{
private:
    std::list<T> _list;//用链表
    std::mutex _mutex;
    std::condition_variable _cond;//条件变量，给消费者使用
public:
    match_queue(){}
    ~match_queue(){}
    //获取元素个数
    int size(){
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    //判断是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    //阻塞线程
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);

    }
    //入队数据并唤醒线程
    void push(const T&data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();//通知所有等待的线程
        
    }
    //出队数据
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()==true){return false;}
        data=_list.front();
        _list.pop_front();
        return true;
    }
    //移除指定的数据
    bool remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }

};


class matcher{
private:
    match_queue<uint64_t> _q_low;//低端局
    match_queue<uint64_t> _q_midrange;//中端局
    match_queue<uint64_t> _q_high;//高端局
    std::thread _th_low;
    std::thread _th_midrange;
    std::thread _th_high;

    room_manager* _rm;
    user_table* _ut;
    online_manager* _om;
private:
    //线程入口函数
    void handle_match(match_queue<uint64_t>& mqueue)
    {
        while(1)
        {
            //1.判断队列人数是否大于2,小于2则阻塞等待
            while(mqueue.size()<2)
            {
                mqueue.wait();
            }
            //2.到这说明人数够了，出队两个玩家
            uint64_t uid1,uid2;
            bool ret=mqueue.pop(uid1);
            if(ret==false){continue;}
            ret=mqueue.pop(uid2);
            if(ret==false){
                this->add(uid1);//这里需要重新把uid1添加到指定队列
                continue;
            }
            //3.校验两个玩家是否在线，如果有人掉线，另一重新添加
            wsserver_t::connection_ptr conn1=_om->get_conn_from_hall(uid1);
            if(conn1.get()==nullptr)//玩家1掉线，重新把玩家2添加到队列
            {
                this->add(uid2);
                continue;
            }

            wsserver_t::connection_ptr conn2=_om->get_conn_from_hall(uid2);
            if(conn2.get()==nullptr)//玩家2掉线，重新把玩家1添加到队列
            {
                this->add(uid1);
                continue;
            }
            //4.为两个玩家创建房间，并将玩家加入房间中
            room_ptr rp=_rm->create_room(uid1,uid2);
            if(rp.get()==nullptr)//创建房间失败，重新添加
            {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            //5.对两个玩家进行响应
            Json::Value resp;
            resp["optype"]="match_success";
            resp["result"]=true;
            std::string body;
            json_util::serialize(resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void th_low_entry()
    {
        return handle_match(_q_low);
    }    
    void th_midrange_entry()
    {
        return handle_match(_q_midrange);
    }    
    void th_high_entry()
    {
        return handle_match(_q_high);
    }    
public:
    matcher(room_manager* rm,user_table* ut,online_manager* om)
        :_rm(rm),_ut(ut),_om(om),
        _th_low(std::thread(&matcher::th_low_entry,this)),
        _th_midrange(std::thread(&matcher::th_midrange_entry,this)),
        _th_high(std::thread(&matcher::th_high_entry,this))
    {
        DLOG("游戏匹配模块初始化完毕！！！");
    }    
    ~matcher(){}

    //添加用户
    bool add(uint64_t uid){
        //根据玩家分数，来判定玩家的匹配队列
        //1.根据用户id获取玩家信息
        Json::Value user;
        bool ret=_ut->select_by_id(uid,user);
        if(ret==false)
        {
            DLOG("获取玩家：%d 信息失败！！",uid);
            return false;
        }
        //2.添加到指定队列中
        int score=user["score"].asInt();
        if(score<2000)
        {
            _q_low.push(uid);
        }
        else if(score>=2000 && score<3000)
        {
            _q_midrange.push(uid);
        }
        else
        {
            _q_high.push(uid);
        }

    }
    //移除用户
    bool del(uint64_t uid){
        //1.根据用户id获取玩家信息
        Json::Value user;
        bool ret=_ut->select_by_id(uid,user);
        if(ret==false)
        {
            DLOG("获取玩家：%d 信息失败！！",uid);
            return false;
        }
        //2.从指定队列中删除
        int score=user["score"].asInt();
        if(score<2000)
        {
            _q_low.remove(uid);
        }
        else if(score>=2000 && score<3000)
        {
            _q_midrange.remove(uid);
        }
        else
        {
            _q_high.remove(uid);
        }
    }
};

#endif